package test.utils;


import org.springframework.beans.FatalBeanException;
import org.springframework.core.ResolvableType;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.IntFunction;

import static org.springframework.beans.BeanUtils.getPropertyDescriptor;
import static org.springframework.beans.BeanUtils.getPropertyDescriptors;

/**
 * Created with IntelliJ IDEA.
 *
 * @ project name : example
 * @ Author: XuLeHuang
 * @ Date: 2022/8/1 11:31
 * @ Description:
 */
public class BeanTobeanLowerCase {
    /**
     * 大小写可以忽略
     * 下划线 _ 被忽略
     * NULL值和空字符串不会覆盖新值
     *
     * @param source
     * @param target
     * @param <T>
     * @return
     */
    public static Object copyPropertiesIgnoreCase(Object sourceObject, Class<?> targetClass) {
        Object targetObject = null;
        try {
             targetObject = targetClass.newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        if (targetObject != null) {
            Class<?> sourceClass = sourceObject.getClass();
            Field[] sourceDeclaredFields = sourceClass.getDeclaredFields();
            Field[] targetClassDeclaredFields = targetClass.getDeclaredFields();
            for (Field sourceDeclaredField : sourceDeclaredFields) {
                String sourceDeclaredFieldName = sourceDeclaredField.getName();
                int sourceDeclaredFieldModifiers = sourceDeclaredField.getModifiers();
                if (Modifier.isPrivate(sourceDeclaredFieldModifiers) && !Modifier.isFinal(sourceDeclaredFieldModifiers)){
                    sourceDeclaredField.setAccessible(true);
                    for (Field targetClassDeclaredField : targetClassDeclaredFields) {
                        String targetClassDeclaredFieldName = targetClassDeclaredField.getName();
                        int targetClassDeclaredFieldModifiers = targetClassDeclaredField.getModifiers();
                        if (Modifier.isPrivate(targetClassDeclaredFieldModifiers) && !Modifier.isFinal(sourceDeclaredFieldModifiers)) {
                            targetClassDeclaredField.setAccessible(true);
                            if (sourceDeclaredFieldName.equalsIgnoreCase(targetClassDeclaredFieldName)) {
                                try {
                                    targetClassDeclaredField.set(targetObject , sourceDeclaredField.get(sourceObject));
                                } catch (IllegalAccessException e) {
                                    e.printStackTrace();
                                }
                            }
                        }
                    }
                }
            }
        }
        return targetObject;
    }

    public static  <T> T copyPropertiesIgnoreCaseV2(Object sourceObject, Object targetObject) {

        if (targetObject != null) {
            Class<?> sourceClass = sourceObject.getClass();
            Field[] sourceDeclaredFields = sourceClass.getDeclaredFields();
            Class<?> targetObjectClass = targetObject.getClass();
            Field[] targetClassDeclaredFields = targetObjectClass.getDeclaredFields();
            for (Field sourceDeclaredField : sourceDeclaredFields) {
                String sourceDeclaredFieldName = sourceDeclaredField.getName();
                int sourceDeclaredFieldModifiers = sourceDeclaredField.getModifiers();
                if (Modifier.isPrivate(sourceDeclaredFieldModifiers) && !Modifier.isFinal(sourceDeclaredFieldModifiers)){
                    sourceDeclaredField.setAccessible(true);
                    for (Field targetClassDeclaredField : targetClassDeclaredFields) {
                        String targetClassDeclaredFieldName = targetClassDeclaredField.getName();
                        int targetClassDeclaredFieldModifiers = targetClassDeclaredField.getModifiers();
                        if (Modifier.isPrivate(targetClassDeclaredFieldModifiers) && !Modifier.isFinal(sourceDeclaredFieldModifiers)) {
                            targetClassDeclaredField.setAccessible(true);
                            if (sourceDeclaredFieldName.equalsIgnoreCase(targetClassDeclaredFieldName)) {
                                try {
                                    targetClassDeclaredField.set(targetObject , sourceDeclaredField.get(sourceObject));
                                } catch (IllegalAccessException e) {
                                    e.printStackTrace();
                                }
                            }
                        }
                    }
                }
            }
        }
        return (T)targetObject;
    }

    /*
    * 源码版忽略大小写反射浅拷贝
    * */
    public static void copyPropertiesIgnoreCaseV3(Object sourceObject, Object targetObject) {
        Assert.notNull(sourceObject, "Source must not be null");
        Assert.notNull(targetObject, "Target must not be null");
        Class<?> actualEditable = targetObject.getClass();
        Class<?> sourceObjectClass = sourceObject.getClass();
        PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
        PropertyDescriptor[] sourcePds = getPropertyDescriptors(sourceObjectClass);
        PropertyDescriptor[] var7 = targetPds;
        PropertyDescriptor[] var10 = sourcePds;
        int var8 = targetPds.length;
        int var11 = sourcePds.length;
        for(int var9 = 0; var9 < var8; ++var9) {
            PropertyDescriptor targetPd = var7[var9];
            Method writeMethod = targetPd.getWriteMethod();
            if (writeMethod != null) {
                for (int var12 = 0 ; var12 < var11 ; ++var12) {
                    PropertyDescriptor sourcePd = var10[var12];
                    if (targetPd.getName().equalsIgnoreCase(sourcePd.getName())){
                        if (sourcePd != null) {
                            Method readMethod = sourcePd.getReadMethod();
                            if (readMethod != null) {
                                ResolvableType sourceResolvableType = ResolvableType.forMethodReturnType(readMethod);
                                ResolvableType targetResolvableType = ResolvableType.forMethodParameter(writeMethod, 0);
                                boolean isAssignable = !sourceResolvableType.hasUnresolvableGenerics() && !targetResolvableType.hasUnresolvableGenerics() ? targetResolvableType.isAssignableFrom(sourceResolvableType) : ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType());
                                if (isAssignable) {
                                    try {
                                        if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
                                            readMethod.setAccessible(true);
                                        }
                                        Object value = readMethod.invoke(sourceObject);
                                        if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
                                            writeMethod.setAccessible(true);
                                        }
                                        writeMethod.invoke(targetObject, value);
                                    } catch (Throwable var18) {
                                        throw new FatalBeanException("Could not copy property '" + targetPd.getName() + "' from source to target", var18);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }

}
